package com.ruanko.action;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;


import org.apache.struts2.ServletActionContext;


import com.opensymphony.xwork2.ActionSupport;

/**
 * 统一下载类
 * 
 * @author lynn
 */
@SuppressWarnings("serial")
public class DownloadAction extends ActionSupport {

    private String   filenames;
    private String   filepaths;
    private String[] filenameArray = null;
    private String[] filepathArray = null;
    private String   filename;
    private String   filepath;
    private SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
        
    
    /**
     * 得到客户端请求的文件名字符串
     * @author lynn
     * @return 客户端请求的文件名字符串

     */
    public String getFilenames() {
        return filenames;
    }
    /**
     * 将客户端请求的文件名字符串set到filenames变量
     * @author lynn
     * @param filenames
     * 2009-10-30   下午11:21:34
     */
    public void setFilenames(String filenames) {
        this.filenames = filenames;
        if (this.filenames.contains("|")) {
            parseFilenamesToArray();
        }
    }
    /**
     * 得到客户端请求的文件路径字符串
     * @author lynn
     * @return 客户端请求的文件路径字符串

     */
    public String getFilepaths() {
        return filepaths;
    }
    /**
     * 将客户端请求的文件路径字符串set到filepaths变量
     * @author lynn
     * @param filepaths
     * 2009-10-30   下午11:21:40
     */
    public void setFilepaths(String filepaths) {
        this.filepaths = filepaths;
        if (this.filepaths.contains("|")) {
            parseFilepathsToArray();
        }
    }
    
    
    
    /**
     * 解析客户端请求下载的文件名
     * @author lynn
     */
    public void parseFilenamesToArray() {
    	try {
    		filenames = new String(filenames.getBytes("ISO-8859-1"),"utf-8");
//    		filenames = filenames.replace(" ", "");
		} catch (Exception e) {
			// TODO: handle exception
		}
        filenameArray = filenames.split("\\|");
    }
    /**
     * 解析客户端请求下载的文件路径
     * @author lynn
     */
    public void parseFilepathsToArray() {
    	try {
    		filepaths = new String(filepaths.getBytes("ISO-8859-1"),"utf-8");
		} catch (Exception e) {
			// TODO: handle exception
		}
        filepathArray = filepaths.split("\\|");
    }
    
    
      
    
    /**
     *  得到下载显示名,对就struts.xml配置文件<param name="contentDisposition">attachment;filename=${filename}</param>
     *  要想正确的显示中文文件名，我们需要对fileName再次编码 否则中文名文件将出现乱码，或无法下载的情况
     * @author lynn
     * @return 返回下载显示名
     */
    public String getFilename() {
        try {
            return new String(filename.getBytes(), "ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return filename;
        }
    }
    
    
    /** 
     *  得到下载文件路径
     * @author lynn
     * @return 返回下载路径
     * 2009-10-30   下午11:27:52
     */
    public String getFilepath(){
        return filepath;
    }
    
    /**
     * 初始化下载文件名
     * @author lynn
     * 2009-10-30   下午11:29:00
     */
    public void initFilename() {
        if(isBaleZip()){
            this.filename = "批量打包下载.zip";
        }else{
            
            try {
            	this.filename = new String(getFilenames().getBytes("ISO-8859-1"),"utf-8");
            	this.filename = this.filename.replace(" ", "");
			} catch (Exception e) {
				this.filename = getFilenames();
			}
        }
        System.out.println("下载文件名:    "+this.filename);
    }
    
    
    /**
     *  初始化下载路径
     * @author lynn
     * 2009-10-30   下午11:30:04
     */
    public void initFilepath() {
        if(isBaleZip()){
        	//创建临时文件夹
            String rootpath = ServletActionContext.getServletContext().getRealPath("/tempfile");
            File dir = new File(rootpath);
            if (dir.exists() == false) {
             dir.mkdirs();
            }
            
            String requestip = ServletActionContext.getRequest().getLocalAddr();
            //this.filepath = "c:\\批量打包下载.zip";
            this.filepath = rootpath+"\\"+requestip+"-"+format.format(new Date())+".zip";
            System.err.println("下载文件路径zip:    "+this.filepath);
        }else{
            this.filepath = getFilepaths();
            try {
            	this.filepath = new String(getFilepaths().getBytes("ISO-8859-1"),"utf-8");
    		} catch (Exception e) {
    			this.filepath = getFilepaths();
    		}
        }
        
        System.out.println("下载文件路径:    "+this.filepath);
    }

    
    /**
     * 判断是否符合打包要求
     * @author lynn
     * @return 否符合打包要求
     * 2009-10-30   上午11:36:09
     */
    public  boolean isBaleZip(){
        boolean isZip = false;
        if(this.filenameArray!= null && this.filepathArray!= null && this.filenameArray.length>0 && this.filenameArray.length==this.filepathArray.length){
             isZip =  true;
        }
        return isZip;
    }
    
    
    /**
     * 压缩文件
     * @author lynn
     * @param zipFilePath  产生的压缩文件路径和名字
     * @param names        传入要进行打包的所有文件名
     * @param paths        传入要进行打包的所有文件路径
     * @throws IOException
     * 2009-10-30   下午11:39:14
     */
    public void baleZip(String zipFilePath,String[] names,String[] paths) throws IOException{
        File f = new File(zipFilePath);
        f.createNewFile();
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(f));
        out.putNextEntry(new ZipEntry("/"));
        for(int i=0;i<paths.length;i++){
            out.putNextEntry(new ZipEntry(names[i])); 
            InputStream in =ServletActionContext.getServletContext().getResourceAsStream(paths[i]);
            int b;
            while ((b = in.read()) != -1) {
                out.write(b);
            }
            in.close();
        }
         out.flush();
         out.close();
    }
    
    
    
    
    /**
     *  返回目标下载文件输入流跟struts2,然后struts2再生成输出流,对应struts.xml的<param name="inputName">downloadFile </param>
     *  但是struts2后台不可能一次性将我们的输入流输出到输出流里面.. 而我们也就是不好控制,例在何时删除产生的临时文件
     * @author lynn
     * @return 目标下载文件输入流
     * 2009-10-30   上午11:45:29
     */
    public InputStream getDownloadFile(){
    	
        initFilename();
        initFilepath();
        InputStream in = null;
        File tempfile = null;
        if(isBaleZip()){
            try {
                baleZip(this.filepath,this.filenameArray,this.filepathArray);
               
                tempfile = new File(this.filepath);
                in =  new FileInputStream(tempfile);
                
            } catch (IOException e) {
                System.out.println(e.getMessage()+"   "+"压缩文件出错!!");
                return null;
            } finally{
                if(tempfile.exists()){
                    tempfile.delete();
                    if(tempfile.exists()){
                        System.out.println("------删除临时文件失败-------");
                        HttpServletRequest request = ServletActionContext.getRequest();
                    	HttpSession session = request.getSession();
                    	session.setAttribute("tempfilePath", filepath);
                    }else{
                        System.out.println("------删除打包产生的临时文件------");
                    }
                }
            }
        }else{
            in  = ServletActionContext.getServletContext().getResourceAsStream(getFilepath());
        }
        return in;
    }
    
    
    
    /**
     * 而这种文件下载方式却是存在安全隐患的， 因为访问者如果精通Struts2的话，它可能使用这样的带有表单参数的地址来访问：
     * http://localhost:8080/disasterSystem/download.action?filename=%E6%B5%8B%E8%AF%95%E4%B8%8B%E8%BD%BD&filepath=/WEB-INF/web.xml
     * 这样的结果就是下载后的文件内容是您系统里面的web.xml的文件的源代码，甚至还可以用这种方式来下载任何其它JSP文件的源码, 这对系统安全是个很大的威胁。
     * 作为一种变通的方法，读者最好是从数据库中进行路径配置，然后把Action类中的设置inputPath的方法统统去掉，简言之就是所有set方法定义
     * 第二种方法,读者可以在execute()方法中进行路径检查，如果发现有访问不属于download下面文件的代码，就一律拒绝，不给他们返回文件内容。
     * 
     * @author lynn
     * @param filepath
     */
    @Override
    public String execute() throws Exception {
        // 文件下载目录路径
        String downloadDir = "/files";
        // 发现企图下载不在 /download 下的文件, 就显示空内容
        if (!filepaths.startsWith(downloadDir)) {
            // 可以抛出一些异常信息
            System.out.println("只能下载files里面的文件!");
            return ERROR;
        }
        return SUCCESS;
    }
}